package com.yql.biz.service.impl;

import cn.hutool.core.util.IdUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.wf.captcha.SpecCaptcha;
import com.wf.captcha.base.Captcha;
import com.yql.biz.common.GlobalConstants;
import com.yql.biz.domain.User;
import com.yql.biz.mapper.UserMapper;
import com.yql.biz.service.LoginService;
import com.yql.biz.vo.CaptchaResultVo;
import com.yql.biz.vo.UserLoginVo;
import com.yql.common.ExceptionCodes;
import com.yql.common.config.JWTConfig;
import com.yql.base.bankSms.BankSms;
import com.yql.base.utils.SocketUtils;
import com.yql.common.core.redis.RedisCache;
import com.yql.common.utils.DateUtils;
import com.yql.common.utils.JwtUtils;
import com.yql.common.utils.StringUtils;
import com.yql.commonextend.exception.CommonException;
import com.yql.commonextend.middleware.sms.Sms;
import com.yql.commonextend.response.CommonResponse;
import com.yql.commonextend.response.ResponseConstant;
import com.yql.commonextend.utils.RequestIdUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.awt.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Service
public class LoginServiceImpl implements LoginService {

    private static Logger log = LoggerFactory.getLogger(LoginServiceImpl.class);

    private static final long ONE_MONTH = (long) 24 * 60 * 60 * 30;

    private static final long EXPIRE_3_MIN = 180L;

    @Autowired
    private RedisCache redisCache;

    @Autowired
    private BankSms bankSms;

    @Autowired
    private SocketUtils socketUtils;

    @Autowired
    private UserMapper userMapper;

    @Value("${captcha.sms.limitTimes}")
    private Integer SmsLimitTimes;

    @Value("${captcha.sms.isBankSms}")
    private Boolean useBankSms;

    @Value("${captcha.image.isUse}")
    private Boolean useImage;

    @Value("${captcha.image.width}")
    private Integer imageW;

    @Value("${captcha.image.height}")
    private Integer imageH;

    @Value("${captcha.image.digit}")
    private Integer digit;

    @Value("${captcha.sms.bank.fpayHost}")
    private String fpayHost;

    @Value("${captcha.sms.bank.fpayPort}")
    private String fpayPort;


    public void sendMsg(String mobile, String imageKey, String imageCode) throws CommonException {

        // 验证是否达到发送限制次数 和 验证码是否可以再次发送
        verifyCount(mobile);
        String code = "";
        if (useBankSms){
            code = bankSms.getMessage(mobile);
        }else {
            code = Sms.getMessage(mobile);
        }
        String countKey = GlobalConstants.captcha.SMS_COUNT_PREFIX + mobile;
        long millis = System.currentTimeMillis();
        redisCache.incrBy(countKey, 1L);
        redisCache.expire(countKey, DateUtils.getSecondsNextEarlyMorning());
        long end = System.currentTimeMillis();
        log.info("保存验证码时间:{}", (millis - end) / 1000);
        // 发送成功保存短信验证码，过期时间5分钟
        String smsKey = GlobalConstants.captcha.SMS_SEND_CODE + mobile;
        redisCache.setCacheObject(smsKey, code, 300L, TimeUnit.SECONDS);
    }


    public CommonResponse getUserToken(UserLoginVo userLoginVo){
        User user = new User();
        QueryWrapper wrapper= new QueryWrapper<>();
        //看是否有手机号来判断该用户是否登录过
        if(StringUtils.isEmpty(userLoginVo.getOpenid())){//若openid为空则代表银行三要素登录
            if (StringUtils.isEmpty(userLoginVo.getC_user_id())
                    && StringUtils.isEmpty(userLoginVo.getC_sign())
                    && StringUtils.isEmpty(userLoginVo.getC_timestamp())){
                return CommonResponse.failed();
            }
            wrapper.eq("c_user_id",userLoginVo.getC_user_id());
            wrapper.eq("c_timestamp",userLoginVo.getC_timestamp());
            wrapper.eq("c_sign",userLoginVo.getC_sign());
            user = userMapper.selectOne(wrapper);
        }else {
            wrapper.eq("openid",userLoginVo.getOpenid());
            user = userMapper.selectOne(wrapper);
        }
        if (user == null || StringUtils.isEmpty(user.getMobile())){
            return CommonResponse.RespWithCodeData(ResponseConstant.RESP_NOT_LOGIN_ERROR_CODE, ResponseConstant.RESP_NOT_LOGIN_ERROR_MESG);
        }
        String userJson = JSONUtil.toJsonStr(User.builder() //根据user_id、openid、c_user_id、mobile生成token
                .userId(user.getUserId()).openid(user.getOpenid()).cUserId(user.getCUserId()).mobile(user.getMobile()).build());
        String token = JwtUtils.createToken(userJson, JWTConfig.getSecret());
        Map<String, Object> map = new HashMap<>();
        map.put("token", token);
        map.put("user", user);
        return CommonResponse.successWithData(map);
    }

    public CommonResponse doLogin(UserLoginVo userLoginVo) throws CommonException{
        //校验登录结束时间是否已到
        User user = new User();
        if (useImage){
            this.verifyCaptchaCode(userLoginVo.getImage_key(), userLoginVo.getImage_code());
        }
        //银行验证手机短信
        CommonResponse verifyCodeRsp = verifySmsCode(GlobalConstants.captcha.SMS_SEND_CODE + userLoginVo.getMobile(),
                userLoginVo.getValidate_code(), userLoginVo.getMobile());
        if (verifyCodeRsp.getCode() != 1000){
            return verifyCodeRsp;
        }
        QueryWrapper wrapper= new QueryWrapper<>();
        //保存用户信息
        if(StringUtils.isEmpty(userLoginVo.getOpenid())) {//若openid为空则代表银行三要素登录
            if (StringUtils.isEmpty(userLoginVo.getC_user_id())
                    && StringUtils.isEmpty(userLoginVo.getC_sign())
                    && StringUtils.isEmpty(userLoginVo.getC_timestamp())) {
                return CommonResponse.failed();
            }
            wrapper.eq("c_user_id",userLoginVo.getC_user_id());
            wrapper.eq("c_timestamp",userLoginVo.getC_timestamp());
            wrapper.eq("c_sign",userLoginVo.getC_sign());
            User bankUser = userMapper.selectOne(wrapper);
            if (bankUser == null){
                user.setCUserId(userLoginVo.getC_user_id());
                user.setCSign(userLoginVo.getC_sign());
                user.setCTimestamp(userLoginVo.getC_timestamp());
                user.setCreateTime(System.currentTimeMillis()/1000);
            }else {
                user = bankUser;
                user.setAddTime(System.currentTimeMillis()/1000);
            }
        }else {
            wrapper.eq("openid",userLoginVo.getOpenid());
            User openUser = userMapper.selectOne(wrapper);
            //手机号的用户是否为空
            QueryWrapper mobileWrapper= new QueryWrapper<>();
            mobileWrapper.eq("mobile",userLoginVo.getMobile());
            User mobileUser = userMapper.selectOne(mobileWrapper);
            if (openUser == null && mobileUser == null){
                user.setOpenid(userLoginVo.getOpenid());
                user.setMobile(userLoginVo.getMobile());
                user.setAddTime(System.currentTimeMillis()/1000);
            }else if (openUser == null && mobileUser != null){
                user = mobileUser;
                user.setOpenid(userLoginVo.getOpenid());
            }
        }
        if (user.getUserId() == null){
            user.setCreateTime(System.currentTimeMillis()/1000);
            user.setAddTime(System.currentTimeMillis()/1000);
            userMapper.insert(user);
        }else {
            user.setCreateTime(System.currentTimeMillis()/1000);
            userMapper.updateById(user);
        }
        String userJson = JSONUtil.toJsonStr(user);
        String token = JwtUtils.createToken(userJson, JWTConfig.getSecret(), ONE_MONTH);
        Map<String, Object> map = new HashMap<>();
        String userMobile = user.getMobile();
        map.put("token", token);
        map.put("user", user);
        return CommonResponse.successWithData(map);
    }


    public CommonResponse getImageCode(){
        SpecCaptcha captcha = new SpecCaptcha(imageW, imageH, digit);
        // 设置字体
        captcha.setFont(new Font("Fresnel", Font.PLAIN, 32));  // 有默认字体，可以不用设置
        captcha.setCharType(Captcha.TYPE_NUM_AND_UPPER);
        // 获取运算的结果
        String result = captcha.text();
        log.info(result);
        String imageKey = IdUtil.simpleUUID();
        String key = GlobalConstants.captcha.SMS_CAPTCHA_PREFIX + imageKey;
        redisCache.setCacheObject(key, result, EXPIRE_3_MIN, TimeUnit.SECONDS);
        return CommonResponse.successWithData(CaptchaResultVo.builder().imageCode(captcha.toBase64()).imageKey(imageKey).build());
    }

    /**
     * 验证短信验证码
     *
     * @param key
     * @param smsCode
     * @param phone
     */
    private CommonResponse verifySmsCode(String key, String smsCode, String phone) throws CommonException{
        Object o = redisCache.getCacheObject(key);
        if (null == o) {
            return CommonResponse.RespWithCodeMsg(ExceptionCodes.MSG_1069.code(), ExceptionCodes.MSG_1069.msg());
        }
        log.info("开始验证短信验证码");
        long millis = System.currentTimeMillis();
        if (useBankSms){
            String validMsgXml = socketUtils.getIdentifyXml(phone, o.toString(), smsCode);
            String sMsg = SocketUtils.getSmsSocket(fpayHost, fpayPort, 6, validMsgXml);
            if ("".equals(sMsg) || null == sMsg) {
                return CommonResponse.RespWithCodeMsg(ExceptionCodes.MSG_1070.code(), ExceptionCodes.MSG_1070.msg());
            }
            String respXml = sMsg.substring(sMsg.indexOf("<Resp>") + 6, sMsg.indexOf("</Resp>"));
            String RetCode = respXml.substring(respXml.indexOf("<RetCode>") + 9, respXml.indexOf("</RetCode>"));
            String RetMsg = respXml.substring(respXml.indexOf("<RetMsg>") + 8, respXml.indexOf("</RetMsg>"));
            log.info(RetMsg);
            if (!"00000".equals(RetCode)) {
                return CommonResponse.RespWithCodeMsg(ExceptionCodes.MSG_1070.code(), ExceptionCodes.MSG_1070.msg());
            }
        }else {
            if (!o.toString().equals(smsCode)){
                return CommonResponse.RespWithCodeMsg(ExceptionCodes.MSG_1070.code(), ExceptionCodes.MSG_1070.msg());
            }
        }
        long endTiem = System.currentTimeMillis();
        log.info("短信验证码验证完成时间{}", millis - endTiem);
        redisCache.deleteObject(key);
        return CommonResponse.success();
    }


    private void verifyCount(String mobile) throws CommonException{
        String requestId = RequestIdUtil.getRequestId();
        // 1.验证发送次数
        String countKey = GlobalConstants.captcha.SMS_COUNT_PREFIX + mobile;
        int sendCount = redisCache.getCacheObject(countKey) == null ? 0 : redisCache.getCacheObject(countKey);
        if (sendCount >= SmsLimitTimes) {
            // 抛出异常
            log.error("requestId:{}手机号{}发送达到每日限制次数，发送失败", requestId, mobile);
            throw new CommonException(ExceptionCodes.MSG_1065);
        }
    }

    private void verifyCaptchaCode(String imageKey, String imageCode) throws CommonException{
        String requestId = RequestIdUtil.getRequestId();
        log.info("requestId:{}进入验证图形验证码方法verifyCaptchaCode:[{}] {}", requestId, imageKey, imageCode);
        String key = GlobalConstants.captcha.SMS_CAPTCHA_PREFIX + imageKey;
        String value = redisCache.getCacheObject(key);
        if (value == null) {
            throw new CommonException(ExceptionCodes.MSG_1067);
        }
        if (!imageCode.equalsIgnoreCase(value)) {
            redisCache.deleteObject(key);
            throw new CommonException(ExceptionCodes.MSG_1066);
        }
        // 删除图形验证码
        redisCache.deleteObject(key);
    }

}
